#pragma once
#include "TcpServer.hpp"
#include "Util.hpp"
#include <memory>
#include <sstream>
#include <unordered_map>

// 应答报头的分隔符
const std::string gspace = " ";
const std::string newline = "\r\n";
const std::string gesp = ": ";

const std::string webroot = "./webroot";
const std::string firstpage = "/index.html";
const std::string page_404 = "/404.html";

// 接受请求的处理
class HttpRequest
{
public:
    HttpRequest()
    {
    }
    // 序列化(不用实现,因为不会享客户端发送请求)
    std::string Serialize()
    {
        return nullptr;
    }
    // 反序列化
    bool DeSerialize(std::string &reqhead, std::string &reqtext)
    {
        std::string line;
        Util::ReadOneLine(reqhead, line, newline);
        std::stringstream ss(line);
        ss >> req_method >> _uri >> _version;

        if (_uri == "/")
        {
            _uri = webroot + firstpage;
        }
        else
        {
            _uri = webroot + _uri;
        }
        std::cout << "req_method: " << req_method << std::endl;
        std::cout << "_uri: " << _uri << std::endl;
        std::cout << "_version: " << _version << std::endl;
        //std::cout << _uri << std::endl;
        return true;
    }
    std::string Uri() { return _uri; }
    ~HttpRequest()
    {
    }

private:
    std::string req_method; // 请求方法
    std::string _uri;       // uri(文件路径)
    std::string _version;   // HTTP版本

    std::unordered_map<std::string, std::string> _headers; // 请求报头
    std::string _blackline;                                // 空行
    std::string _text;                                     // 正文
};

// 应答处理
class HttpResponse
{
public:
    HttpResponse() : _blankline(newline), _version("HTTP/1.0")
    {
    }
    // 序列化(实现)
    std::string Serialize()
    {
        std::string result;
        // 状态行信息
        result = _version + gspace + std::to_string(_code) + gspace + _desc + newline;
        // 响应报头
        for (auto &head : _headers)
        {
            result += (head.first + gesp + head.second + newline);
        }
        // 空行
        result += _blankline;
        // 正文
        result += _text;
        return result;
    }
    // 目标文件
    void SetTargetFile(std::string target)
    {
        _targetfile = target;
    }
    // 根据状态码码,拿到状态码描述
    void SetCode(int code)
    {
        switch (code)
        {
        case 200:
            _desc = "OK";
            break;
        case 404:
            _desc = "Not Found";
            break;
        default:
            break;
        }
    }
    // 获取报头
    void SetHeader(const std::string &key, const std::string &value)
    {
        auto iter = _headers.find(key);
        if (iter != _headers.end())
        {
            return;
        }
        _headers.insert(std::make_pair(key, value));
    }
    std::string Uri2Suffix(const std::string& _targetfile)
    {
        auto pos = _targetfile.rfind(".");
        if(pos == std::string::npos)
        {
            return "text/html";
        }
        std::string tmp = _targetfile.substr(pos);
        if(tmp == ".html" || tmp == ".htm")
            return "text/html";
        else if(tmp == ".jpg")
            return "image/jpeg";
        else if(tmp == "png")
            return "image/png";
        else
            return "";
    }
    // 读取正文内容
    void MakeResponse()
    {
        bool n = Util::tools(_targetfile, _text);
        //std::cout << "文件打开1: " << n <<std::endl;
        if (!n)
        {
            LOG(LogLevel::WARNING) << "client want get: " << _targetfile << " but not found";
            _code = 404;
            SetCode(_code);
            _targetfile = webroot + page_404;
            Util::tools(_targetfile, _text);
        }
        else
        {
            _code = 200;
            SetCode(_code);
        }
        int filesize = Util::FileSize(_targetfile);
        // std::string suffix = Uri2Suffix(_targetfile);
        // SetHeader("Content-Type", suffix);
        //std::cout << "文件打开2: " << filesize << std::endl;
        //这里浏览器可能会报长度不匹配错误:
        //浏览器在请求时(部分浏览器)一次请求要访问两个资源，一个是指定的(如首页)资源,另一个是./wwwroot/favicon.ico资源(既网站标签上的小图标，头标)
        //我们这里没有处理头标,在请求时就会404, 所以在浏览器中就会得到两种网页,一种是我们的首页,另一种是404网页,有可能会把两个网页的长度搞混le，所以会报出错误
        //GET http://122.51.234.32:8081/ net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)
        //不是上面那个错,禁用掉长度才会出现./wwwroot/favicon.ico
        //真正的错误是使用文件的读取方式,使用二进制的读取方法才正确,因为有些HTTP代码中的数据是使用二进制进行存储的,在二进制中读到0了以后可能会让文件误以为读到了结尾导致长度不匹配
        SetHeader("Content-Length", std::to_string(filesize));
    }
    // 反序列化
    bool ReSerialize()
    {
        return true;
    }
    ~HttpResponse()
    {
    }

public:
    std::string _version; // HTTP版本
    int _code;            // 状态码
    std::string _desc;    // 状态码描述

    std::unordered_map<std::string, std::string> _headers; // 响应报头
    std::string _blankline;                                // 空行
    std::string _text;                                     // 正文
    std::string _targetfile;                               // 目标文件
};
// http协议
class Http
{
public:
    // 构造函数(HTTP底层基于tcp)
    Http(uint16_t port) : tsvrp(std::make_unique<TcpServer>(port))
    {
    }
    // 判断是否读到了完整的报头
    bool Decode(std::string &message, std::string &head, std::string &text)
    {
        std::string hend_end = newline + newline;
        auto pos = message.find(hend_end);
        if (pos == std::string::npos)
        {
            LOG(LogLevel::DEBUG) << "Decode error";
            // 未读取到一个完成的报头
            return false;
        }
        // 一定读取到至少一个完整的报头
        // 找到正文的长度
        std::string tmp = "Content-Length" + gesp;
        auto pos1 = message.find(tmp); // 定位到Content-Length行
        head = message.substr(0, pos);
        if (pos1 == std::string::npos)
        {
            // 没有正文
            return true;
        }
        auto pos2 = message.find(newline, pos1); // 从pos1开始找换行符
        pos1 = pos1 + tmp.size();
        int text_len = std::stoi(message.substr(pos1, pos2));
        // 报头字符串

        int sum_len = head.size() + text_len + 2 * newline.size();
        if (message.size() < sum_len)
        {
            return false;
        }
        // 一定有一套完整的请求

        text = message.substr(pos + 2 * newline.size(), sum_len);
        message.erase(0, sum_len);
        return true;
    }
    // http协议的接收和返回
    void HandlHttpResquest(std::shared_ptr<Socket> &sock, Inet_Addr addr)
    {
        // 这里应该是循环读取 不是我读取一次就读取了 我就发了半个报文 然后读取了一次就结束了
        std::string message; // 获取读到的字符串
        // while (1)
        // {
            int n = sock->Recv(&message); // 无法确定读到的是否为一个完整的请求,可以通过空行来读取完整的报头,再根据报头中的Length提取内容
            // std::cout << "-------------------------------" << std::endl;
            // std::cout << message << std::endl;
            // std::cout << "-------------------------------" << std::endl;
            if (n > 0)
            {
                //std::cout << "读取成功" << message << std::endl;
                // 读取成功
                std::string head;
                std::string text("NULL");

                // 判断读取到的信息是否完整
                // if (Decode(message, head, text))
                // {
                    //std::cout << "head: " << head << std::endl;
                    HttpRequest req;
                    req.DeSerialize(message, text);
                    HttpResponse res;
                    res.SetTargetFile(req.Uri());
                    res.MakeResponse();
                    std::string result = res.Serialize();

                    sock->Send(result);
                }
            // }
            // else
            // {
            //     break;
            // }
        // }
    }
    // 服务器的入口
    void Start()
    {
        // 将Http协议的方法绑定给TcpServer,
        // 使得与客户端建立好链接后直接进入Http服务模块进行对应的请求处理
        tsvrp->Start([this](std::shared_ptr<Socket> &sock, Inet_Addr addr)
                     { this->HandlHttpResquest(sock, addr); });
    }
    ~Http()
    {
    }

private:
    std::unique_ptr<TcpServer> tsvrp;
};